#include  <stddef.h>
#include <stdio.h>
#include  <stdlib.h>
#include<string.h>
#include <jpeglib.h>
#include "./readbitmap.h"
#include "defs.h"

#include "memory.h"


// AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB
// 76543210765432107654321076543210
#define BMP_RGBA32(r,g,b,a)  (U4)( ((U4)(U1)(r)<<16) | ((U4)(U1)(g)<<8) | (U4)(U1)(b) | ((U4)(U1)(a)<<24) )
#define BMP_RGB24(r,g,b)     (U4)( ((U4)(U1)(r)<<16) | ((U4)(U1)(g)<<8) | (U4)(U1)(b) )
 
// XRRRRRGGGGGBBBBB
// 0432104321043210
#define BMP_RGBA32TOBMP16(c) (U2)( (((U4)(c)>>9) & 0x7C00u) | (((U4)(c)>>6) & 0x03E0u) | (((U4)(c)>>3) & 0x001F) )

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef unsigned char  U1;
typedef unsigned short U2;
typedef unsigned long  U4;
 
typedef unsigned char  BOOL;
#define TRUE  (0xFFu)
#define FALSE (0x00u)
 
 void vd_SerializeLittleEndianU2(U1 * u1_ap_serial, U2 u2_a_value)
{
	do
	{
		if(u1_ap_serial == NULL)
			break;
 
		u1_ap_serial[0] = (U1)u2_a_value;
		u1_ap_serial[1] = (U1)(u2_a_value >> 8);
	} while(FALSE);
}
 
void vd_SerializeLittleEndianU3(U1 * u1_ap_serial, U4 u4_a_value)
{
	do
	{
		if(u1_ap_serial == NULL)
			break;
 
		u1_ap_serial[0] = (U1)u4_a_value;
		u1_ap_serial[1] = (U1)(u4_a_value >> 8);
		u1_ap_serial[2] = (U1)(u4_a_value >> 16);
	} while(FALSE);
}
 
void vd_SerializeLittleEndianU4(U1 * u1_ap_serial, U4 u4_a_value)
{
	do
	{
		if(u1_ap_serial == NULL)
			break;
 
		u1_ap_serial[0] = (U1)u4_a_value;
		u1_ap_serial[1] = (U1)(u4_a_value >> 8);
		u1_ap_serial[2] = (U1)(u4_a_value >> 16);
		u1_ap_serial[3] = (U1)(u4_a_value >> 24);
	} while(FALSE);
}
typedef struct {
	U4 u4_image_width;
	U4 u4_image_height;
	U4 u4_widthbyte;
	U4 u4_image_size;
	U2 u2_bitcount;
	U2 u2_palette_size;
	U1* u1_p_image_data;
} ST_BITMAP;
 
ST_BITMAP * st_g_CreateBitmap(U4 u4_a_width, U4 u4_a_height, U2 u2_a_bitcount,char  *data)
{
	BOOL b_t_success = FALSE;
	ST_BITMAP * st_tp_bitmap = NULL;
	U4 u4_t_widthbyte = 0;
	U4 u4_t_imagesize = 0;
	
	do
	{
		if((u4_a_width == 0) || (u4_a_width > 4096))
			break;
		
		if((u4_a_height == 0) || (u4_a_height > 4096))
			break;
		
		if((u2_a_bitcount != 16) && (u2_a_bitcount != 24) && (u2_a_bitcount != 32))
			break;
 
		// 4-byte aligned bytes for a line of image data
		u4_t_widthbyte = (u4_a_width * u2_a_bitcount + 31) / 32 * 4;
		u4_t_imagesize = (u4_t_widthbyte * u4_a_height);
 
		st_tp_bitmap = malloc(sizeof(ST_BITMAP) + u4_t_imagesize); // alloc together
		if(st_tp_bitmap == NULL)
			break;
 
		memset(st_tp_bitmap, 0, sizeof(ST_BITMAP) + u4_t_imagesize);
		
		st_tp_bitmap->u4_image_width = u4_a_width;
		st_tp_bitmap->u4_image_height = u4_a_height;
		st_tp_bitmap->u4_widthbyte = u4_t_widthbyte;
		st_tp_bitmap->u4_image_size = u4_t_imagesize;
		st_tp_bitmap->u2_bitcount = u2_a_bitcount;
		st_tp_bitmap->u2_palette_size = 0;
		// pointer to the address next to the struct
		st_tp_bitmap->u1_p_image_data = (U1 *)st_tp_bitmap + sizeof(ST_BITMAP);
		//add data
        memcpy(st_tp_bitmap->u1_p_image_data,data,PIC_W*PIC_H*3);
		b_t_success = TRUE;
	} while(FALSE);
	
	return st_tp_bitmap;
}
 

void vd_g_SaveBitmap(const ST_BITMAP * st_ap_bitmap, const char * sz_ap_path)
{   
    printf("try to save bmp file in %s\n",sz_ap_path);
	BOOL b_t_success = FALSE;
 
	U1 u1_tp_bitmap_header[] =
	{
		0x42, 0x4D, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, //  2 AA->FileSize
		0x00, 0x00, 0xBB, 0xBB, 0xBB, 0xBB, 0x28, 0x00, // 10 BB->OffBits
		0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xDD, 0xDD, // 18 CC->Width
		0xDD, 0xDD, 0x01, 0x00, 0xEE, 0xEE, 0x00, 0x00, // 22 DD->Height
		0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, // 28 EE->BitCount
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 34 FF->ImageSize
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};
 
	FILE *file_bitmap = NULL;
	
	U4 u4_t_y;
	U4 u4_t_pixel_offset = 0;
 
	do
	{
		if(st_ap_bitmap == NULL)
			break;
		
		if((st_ap_bitmap->u2_bitcount != 16) && (st_ap_bitmap->u2_bitcount != 24) && (st_ap_bitmap->u2_bitcount != 32))
			break;
 
		if(sz_ap_path == NULL)
			break;
	
		file_bitmap = fopen(sz_ap_path, "wb");
		if(file_bitmap == NULL)
			break;
 
		// set bitmap head info
		vd_SerializeLittleEndianU4(&u1_tp_bitmap_header[2], sizeof(u1_tp_bitmap_header) + st_ap_bitmap->u4_image_size);
		vd_SerializeLittleEndianU4(&u1_tp_bitmap_header[10], sizeof(u1_tp_bitmap_header));
		vd_SerializeLittleEndianU4(&u1_tp_bitmap_header[18], st_ap_bitmap->u4_image_width);
		vd_SerializeLittleEndianU4(&u1_tp_bitmap_header[22], st_ap_bitmap->u4_image_height);
		vd_SerializeLittleEndianU2(&u1_tp_bitmap_header[28], st_ap_bitmap->u2_bitcount);
		vd_SerializeLittleEndianU4(&u1_tp_bitmap_header[34], st_ap_bitmap->u4_image_size);
 
		// write bitmap file head
		fwrite(u1_tp_bitmap_header, sizeof(u1_tp_bitmap_header), 1L, file_bitmap);
		
		// write bitmap image data, bottom to top
		u4_t_pixel_offset = st_ap_bitmap->u4_image_height * st_ap_bitmap->u4_widthbyte;
		for(u4_t_y = 0; u4_t_y < st_ap_bitmap->u4_image_height; u4_t_y++)
		{
			u4_t_pixel_offset -= st_ap_bitmap->u4_widthbyte;
			fwrite(&st_ap_bitmap->u1_p_image_data[u4_t_pixel_offset], st_ap_bitmap->u4_widthbyte, 1L, file_bitmap);
		}
		
		b_t_success = TRUE;
 
	} while(0);
	
	if(file_bitmap)
		fclose(file_bitmap);
}
int write_bmpfile(U1* u1_p_image_data){
    ST_BITMAP * bitmap=st_g_CreateBitmap(PIC_W,PIC_H,32,u1_p_image_data);
    // ST_BITMAP * bitmap=st_g_CreateBitmap(PIC_W,PIC_H,24,u1_p_image_data);
    vd_g_SaveBitmap(bitmap,"../data/dump_image/tmpfs/2272_de.bmp");
    return 0;
}



//  JSAMPLE *image_buffer;
unsigned char  *image_buffer;
int image_buffer_index;
//https://raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/main/example.txt
// data
void init_data(char  * path)
{   
    // Btimap
    BmpInfo  * info = readBmp(path);
    int iWidth = PIC_W;
    int iHeight = 1080;
    image_buffer=(uint8_t *)calloc(3*PIC_W*PIC_H,sizeof(uint8_t));
    memset(image_buffer,0,PIC_W*PIC_H*3);
    int index=0;
    // int index_rgba=0;
    // while (index<3*PIC_W*PIC_H)
    // {
    //    image_buffer[index++]=info->rgbData[index_rgba++];
    //  if((index_rgba!=0 )&& ((index_rgba%3)==0))
    //      index_rgba++;
    // }
    memcpy(image_buffer,info->rgbData,3*PIC_W*PIC_H);
    // printf("image_buffer size %ld\n",size);
}

void init_data_risual(char * curPic,char *refPic){
     ResidualMB * R=  caculatePicResidual( curPic,refPic, PIC_MB_W,PIC_MB_H);
    int data_MB_modified=1;
    if(data_MB_modified==1){
        init_data_MB_modified(curPic,R);

        return;
    }
   
    unsigned size=PIC_H*PIC_W*3;
    image_buffer=(uint8_t *)calloc(size,sizeof(uint8_t));
     memset(image_buffer,0,PIC_W*PIC_H*3);
    // image_buffer=(uint8_t *)calloc(900000,sizeof(uint8_t));
    // uint8_t * rgbData = (uint8_t *) calloc(size, sizeof(uint8_t));
    int i=0,j=0,k=0;
    unsigned index=0;
    while (R[k].yuvDataResidual!=NULL&&R[k].yuvDataResidual!=0)
    {
        if(k>172)
          break;
        for ( i = 0; i < block32; i++){
          for(j=0;j<block32;j++){
            /* code */
            image_buffer[index++]=R[k].yuvDataResidual[i][j][0];
            image_buffer[index++]=R[k].yuvDataResidual[i][j][1];
            image_buffer[index++]=R[k].yuvDataResidual[i][j][2];
            printf("%d %d %d    ",R[k].yuvDataResidual[i][j][0],R[k].yuvDataResidual[i][j][1],R[k].yuvDataResidual[i][j][2]);
         }
         printf("\n");
         }
          k++;
    }

    printf("image_buffer residule size %ld\n",index);
    return;

}



void init_data_MB_modified(char * curPic, ResidualMB * R){
           unsigned size=PIC_H*PIC_W*3;
       image_buffer=(uint8_t *)calloc(size,sizeof(uint8_t));
       memset(image_buffer,0,size);
    BmpInfo *bmpCur = readBmp(curPic);
    int i=0,j=0,k=0;

    int index=0;
    for(k=0;k<172;k++){
             int R_idx=R[i].MBIdx;
             int Y=R_idx/PIC_MB_W;
             int X=R_idx%PIC_MB_W;
             int*** yuv_data=getYUVMBFromBmp(bmpCur,X,Y);
        for ( i = 0; i < block32; i++){
          for(j=0;j<block32;j++){
            image_buffer[index++]=yuv_data[i][j][0];
            image_buffer[index++]=yuv_data[i][j][1];
            image_buffer[index++]=yuv_data[i][j][2];
              printf("%d %d %d    ",yuv_data[i][j][0],yuv_data[i][j][1],yuv_data[i][j][2]);
            }
                }
      printf("\n");
    }
    printf("image_buffer MB size %ld\n",index);


    //  FILE * bmpfile = fopen(curPic, "rb");
    //  uint8_t* rgbdata = getRgbData(bmpfile);
    //  if (bmpfile == NULL) oops("can not open file");
    //    unsigned size=PIC_H*PIC_W*3;
    //   image_buffer=(uint8_t *)calloc(size,sizeof(uint8_t));
    //    memset(image_buffer,0,size);
    //   unsigned copy_size=172*block32*block32*3;
    // int i=0;
    // for(i=0;i<copy_size;i++){
    //     // image_buffer[i]='0'+rgbdata[i];
    //     image_buffer[i]=''
    // }
    // //   memcpy(image_buffer,(char *)rgbdata,copy_size);
    // //   printf("image_buffer size %ld\n",strlen(rgbdata));

    //   printf("image_buffer size %ld\n",strlen(image_buffer));
}

// 参考链接：https://blog.csdn.net/beyondforme/article/details/82771160
//https://www.cnblogs.com/xiaoxiaoboke/archive/2012/02/13/2349763.html
int jpegCompress(char *dPath,char * sPath,int rusidual){
    //  unsigned char *pDataConv = getRgbData(path);
    if(!rusidual)
        init_data(sPath);
    else
        init_data_risual("../data/dump_image/tmpfs/2272.bmp","../data/dump_image/tmpfs/2273.bmp");
    //申请并初始化jpeg压缩对象
    struct jpeg_compress_struct cinfo;
    // 声明错误处理器，并赋值给cinfo.err域
    struct jpeg_error_mgr jem;
    cinfo.err = jpeg_std_error(&jem);
    jpeg_create_compress(&cinfo);
    //指定压缩后的图像所存放的目标文件，注意，目标文件应以二进制模式打开
    FILE *f=fopen(dPath,"wb");
    if (f==NULL) {
    //     delete [] data;
    //    delete [] pDataConv;
        printf("bad path %s\n",dPath);
        return 0;
    }
    jpeg_stdio_dest(&cinfo, f);
    //设置压缩参数
    cinfo.image_width = PIC_W;
    cinfo.image_height=PIC_H;
    cinfo.input_components = 3;// 在此为1,表示灰度图， 如果是彩色位图，则为3 
    cinfo.in_color_space = JCS_RGB; //cinfo_GRAYSCALE表示灰度图，JCSfo_RGB表示彩色图像
    jpeg_set_defaults(&cinfo); 
    jpeg_set_quality (&cinfo, 80, 1);

    jpeg_start_compress(&cinfo, TRUE);
    JSAMPROW row_pointer[1];//一行位图
    int row_stride;      // 每一行的字节数
    row_stride = cinfo.image_width*3;  // 如果不是索引图,此处需要乘以3
    // 对每一行进行压缩
    while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
    (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }


    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);

}


int jpegDecompress(char *path){
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    FILE *f = fopen(path,"rb");
    if (f==NULL)
    {
     printf("Open file error!\n");
     return;
    }
    jpeg_stdio_src(&cinfo, f);
    jpeg_read_header(&cinfo, TRUE);
    char *data = (char *) calloc (sizeof(int),cinfo.image_width*cinfo.image_height*cinfo.num_components);

    jpeg_start_decompress(&cinfo);


    JSAMPROW row_pointer[1];
    while (cinfo.output_scanline < cinfo.output_height)
    {
        // row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.image_width*cinfo.num_components];
        row_pointer[0] = &data[(cinfo.output_scanline)*cinfo.image_width*cinfo.num_components];
        jpeg_read_scanlines(&cinfo,row_pointer , 1);
        // put_scanline_someplace(buffer[0], row_stride);
    }
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(f);
    write_bmpfile(data);

}
char *
itoa(int i)
{
char str1[20];
sprintf(str1,"%d",i);

return str1;
}
void append_image_buffer(int append){
     char *tmp;
     int i=0;
     tmp=itoa(append);
     int len=strlen(tmp);
     for(i=0;i<len;i++){
         image_buffer[image_buffer_index]=tmp[i];
         image_buffer_index++;
     }
}

void compress_Block(int *** data,char *output_path){
    image_buffer=(uint8_t *)calloc(3*PIC_W*PIC_H,sizeof(uint8_t));
    image_buffer_index=0;
    memset(image_buffer,0,PIC_W*PIC_H*3);
    int index[1];
    int j=0,k=0,i=0;
   
    int offset=0;
    for ( i = 0; i < block32; i++){
        for(j=0;j<block32;j++){
        /* code */
        image_buffer[image_buffer_index++]=data[i][j][0]+offset;
        image_buffer[image_buffer_index++]=data[i][j][1]+offset;
        image_buffer[image_buffer_index++]=data[i][j][2]+offset;
        // image_buffer[image_buffer_index++]=data[i][j][0];
        // image_buffer[image_buffer_index++]=data[i][j][1];
        // image_buffer[image_buffer_index++]=data[i][j][2];
        // append_image_buffer(data[i][j][0]);
        // append_image_buffer(data[i][j][1]);
        // append_image_buffer(data[i][j][2]);
        // printf("%d %d %d    ",R[k].yuvDataResidual[i][j][0],R[k].yuvDataResidual[i][j][1],R[k].yuvDataResidual[i][j][2]);
        }
    }
    //  printf("imgbuffer_append %s",image_buffer);
    printf("total data size %d \n",image_buffer_index);
    // printf("total data size %d \n",strlen(image_buffer));

     //申请并初始化jpeg压缩对象
    struct jpeg_compress_struct cinfo;
    // 声明错误处理器，并赋值给cinfo.err域
    struct jpeg_error_mgr jem;
    cinfo.err = jpeg_std_error(&jem);
    jpeg_create_compress(&cinfo);
    //指定压缩后的图像所存放的目标文件，注意，目标文件应以二进制模式打开
    FILE *f=fopen(output_path,"wb");
    if (f==NULL) {
    //     delete [] data;
    //    delete [] pDataConv;
        printf("bad path %s\n",output_path);
        return 0;
    }
    jpeg_stdio_dest(&cinfo, f);
    //设置压缩参数
    cinfo.image_width = block32;
    cinfo.image_height=block32;
    cinfo.input_components = 3;// 在此为1,表示灰度图， 如果是彩色位图，则为3 
    cinfo.in_color_space = JCS_RGB; //cinfo_GRAYSCALE表示灰度图，JCSfo_RGB表示彩色图像
    jpeg_set_defaults(&cinfo); 
    jpeg_set_quality (&cinfo, 80, 1);

    jpeg_start_compress(&cinfo, TRUE);
    JSAMPROW row_pointer[1];//一行位图
    int row_stride;      // 每一行的字节数
    row_stride = cinfo.image_width*3;  // 如果不是索引图,此处需要乘以3
    // 对每一行进行压缩
    while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
    (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }


    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
}
void put_scanline_someplace(char * buffer,int row_stride){

}
void printData(unsigned char *data,int k){
    int len=strlen(data);
    int i=0;
    for(i=k;i<block32*block32*3;i=i+3){
        printf("%d ",data[i]);
        if(i!=0&&i % 32*3 ==0)
          printf("\n");
    }
    printf("data  print %d\n",block32*block32*3);
}
unsigned char * decompressOneBlock(char *filename){
      struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);
    FILE *f = fopen(filename,"rb");
    if (f==NULL)
    {
     printf("Open file error!\n");
     return;
    }
    jpeg_stdio_src(&cinfo, f);
    jpeg_read_header(&cinfo, TRUE);
    unsigned char *data = (unsigned char *) calloc (sizeof(int),cinfo.image_width*cinfo.image_height*cinfo.num_components);

    jpeg_start_decompress(&cinfo);


    JSAMPROW row_pointer[1];
    while (cinfo.output_scanline < cinfo.output_height)
    {
        // row_pointer[0] = &data[(cinfo.output_height - cinfo.output_scanline-1)*cinfo.image_width*cinfo.num_components];
        row_pointer[0] = &data[(cinfo.output_scanline)*cinfo.image_width*cinfo.num_components];
        jpeg_read_scanlines(&cinfo,row_pointer , 1);
    }
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(f);
    return data;
}
void compress_Block_16X16(char * input_path,char *output_path){
    int size=16*16;
    int length=16;
    image_buffer=(uint8_t *)calloc(size,sizeof(uint8_t));
    image_buffer_index=0;
    memset(image_buffer,0,size);
    int index[1];
    int j=0,k=0,i=0;
    int *block16x16_1=readBlock16x16(input_path);
    int offset=0;
    for ( i = 0; i < length*length; i++){
        image_buffer[image_buffer_index++]=block16x16_1[i];
    }
    //  printf("imgbuffer_append %s",image_buffer);
    printf("total data size %d \n",image_buffer_index);
    // printf("total data size %d \n",strlen(image_buffer));

     //申请并初始化jpeg压缩对象
    struct jpeg_compress_struct cinfo;
    // 声明错误处理器，并赋值给cinfo.err域
    struct jpeg_error_mgr jem;
    cinfo.err = jpeg_std_error(&jem);
    jpeg_create_compress(&cinfo);
    //指定压缩后的图像所存放的目标文件，注意，目标文件应以二进制模式打开
    FILE *f=fopen(output_path,"wb");
    if (f==NULL) {
    //     delete [] data;
    //    delete [] pDataConv;
        printf("bad path %s\n",output_path);
        return 0;
    }
    jpeg_stdio_dest(&cinfo, f);
    //设置压缩参数
    cinfo.image_width = length;
    cinfo.image_height=length;
    cinfo.input_components = 3;// 在此为1,表示灰度图， 如果是彩色位图，则为3 
    cinfo.in_color_space = JCS_RGB; //cinfo_GRAYSCALE表示灰度图，JCSfo_RGB表示彩色图像
    jpeg_set_defaults(&cinfo); 
    jpeg_set_quality (&cinfo, 80, 1);

    jpeg_start_compress(&cinfo, TRUE);
    JSAMPROW row_pointer[1];//一行位图
    int row_stride;      // 每一行的字节数
    row_stride = cinfo.image_width*3;  // 如果不是索引图,此处需要乘以3
    // 对每一行进行压缩
    while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride];
    (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }


    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
}